/*  ----------------------------------------------------------------------------
 *  Arello Mobile
 *  Fast 3d Engine
 *  ----------------------------------------------------------------------------
 * Except where otherwise noted, this work is licensed under
 * http://creativecommons.org/licenses/by/3.0
 */

#include "Hardware.h"
#include "TextureCubic.h"
#include <WindowImpl.h>
#include <ColorPacked.h>
#include <Gluck.h>
#include <logs/Log.h>
#include <math/utl.h>
#include <system/assert.h>	
#include <sstream>

//#define SPEED_TEST

namespace liba
{
namespace lib3d
{
namespace hard
{
namespace directx8
{

static const std::string WINDOWED_NAME = "No (Window mode)";
static const std::string ANTIALIASING_NONE_NAME = "None";
static const std::string ANTIALIASING_2X_NAME = "2xSamples (Medium)";
static const std::string ANTIALIASING_4X_NAME = "4xSamples (High)";

static HRESULT hr;

static const Vector table_factors(1,1/16.0,1/4.0);

template<class T>
int GlobalRelease(T ** object)
{
	int ref_count = 0;
	if( *object )
		ref_count = (*object)->Release();
	(*object) = 0;
	return ref_count;
}

static float gag_const[]= { 0, 0.5, 1, 2 };

converter::EnumPair<char,D3DFORMAT> Hardware::d3dformat_names[] =
{
	converter::MakeEnumPair("D3DFMT_UNKNOWN", D3DFMT_UNKNOWN),

	converter::MakeEnumPair("D3DFMT_A2B10G10R10", D3DFMT_A2B10G10R10),
	converter::MakeEnumPair("D3DFMT_A8R8G8B8", D3DFMT_A8R8G8B8),
	converter::MakeEnumPair("D3DFMT_X8R8G8B8", D3DFMT_X8R8G8B8),
	converter::MakeEnumPair("D3DFMT_R8G8B8", D3DFMT_R8G8B8),
	converter::MakeEnumPair("D3DFMT_A1R5G5B5", D3DFMT_A1R5G5B5),
	converter::MakeEnumPair("D3DFMT_X1R5G5B5", D3DFMT_X1R5G5B5),
	converter::MakeEnumPair("D3DFMT_R5G6B5", D3DFMT_R5G6B5),
	converter::MakeEnumPair("D3DFMT_A4R4G4B4", D3DFMT_A4R4G4B4),

	converter::MakeEnumPair("D3DFMT_D24S8", D3DFMT_D24S8),
	converter::MakeEnumPair("D3DFMT_D32", D3DFMT_D32),
	converter::MakeEnumPair("D3DFMT_D24X8", D3DFMT_D24X8),
	converter::MakeEnumPair("D3DFMT_D16", D3DFMT_D16),
	converter::MakeEnumPair("D3DFMT_D24X4S4", D3DFMT_D24X4S4),
	converter::MakeEnumPair("D3DFMT_D16_LOCKABLE", D3DFMT_D16_LOCKABLE),
	converter::MakeEnumPair("D3DFMT_D15S1", D3DFMT_D15S1),
	0
};

bool Hardware::fill_dependent_parameters(Parameters * parameters)
{
	std::vector<D3DFORMAT> disp_formats;
	std::vector<D3DFORMAT> back_formats;

	if( style == SLOW )
	{
		back_formats.push_back( D3DFMT_R5G6B5 );
		disp_formats.push_back( D3DFMT_R5G6B5 );
		back_formats.push_back( D3DFMT_X1R5G5B5 );
		disp_formats.push_back( D3DFMT_X1R5G5B5 );
		back_formats.push_back( D3DFMT_A1R5G5B5 );
		disp_formats.push_back( D3DFMT_X1R5G5B5 );
	}

	back_formats.push_back( D3DFMT_A8R8G8B8 );
	disp_formats.push_back( D3DFMT_X8R8G8B8 );

	back_formats.push_back( D3DFMT_X8R8G8B8 );
	disp_formats.push_back( D3DFMT_X8R8G8B8 );

	if( style != SLOW )
	{
		back_formats.push_back( D3DFMT_R5G6B5 );
		disp_formats.push_back( D3DFMT_R5G6B5 );
		back_formats.push_back( D3DFMT_X1R5G5B5 );
		disp_formats.push_back( D3DFMT_X1R5G5B5 );
		back_formats.push_back( D3DFMT_A1R5G5B5 );
		disp_formats.push_back( D3DFMT_X1R5G5B5 );
	}
	
	parameters->display_format = D3DFMT_UNKNOWN;
	parameters->color_format = D3DFMT_UNKNOWN;
	if( parameters->is_fullscreen )
	{
		for( int df = 0; df != back_formats.size() && df != disp_formats.size(); ++df )
		{
			if( direct3d->CheckDeviceType( adapterInfo.AdapterNumber, adapterInfo.DeviceType, disp_formats[df], back_formats[df], false ) == D3D_OK )
			{
				parameters->display_format = disp_formats[df];
				parameters->color_format = back_formats[df];
				break;
			}
		}
	}
	else
	{
		D3DDISPLAYMODE display_mode;
		direct3d->GetAdapterDisplayMode( adapterInfo.AdapterNumber, &display_mode );
		for( int df = 0; df != back_formats.size(); ++df )
		{
			if( direct3d->CheckDeviceType( adapterInfo.AdapterNumber, adapterInfo.DeviceType, display_mode.Format, back_formats[df], true ) == D3D_OK )
			{
				parameters->display_format = display_mode.Format;
				parameters->color_format = back_formats[df];
				break;
			}
		}
	}
	if( parameters->display_format == D3DFMT_UNKNOWN || parameters->color_format == D3DFMT_UNKNOWN )
		return false;

	std::vector<D3DFORMAT> rt_formats;
	rt_formats.push_back( D3DFMT_A2B10G10R10 );
	rt_formats.push_back( D3DFMT_X8R8G8B8 );
	rt_formats.push_back( D3DFMT_R8G8B8 );
	rt_formats.push_back( D3DFMT_A8R8G8B8 );
//	if( !parameters->need_8_rt_color_channel_bits )
	{
		rt_formats.push_back( D3DFMT_R5G6B5 );
		rt_formats.push_back( D3DFMT_X1R5G5B5 );
		rt_formats.push_back( D3DFMT_A1R5G5B5 );
	}

	std::vector<D3DFORMAT> z_formats;
//	if( parameters->shadow_passes == 0 )
	{
		z_formats.push_back( D3DFMT_D32 );
		z_formats.push_back( D3DFMT_D24X8 );
		z_formats.push_back( D3DFMT_D16 );
		z_formats.push_back( D3DFMT_D16_LOCKABLE );
	}
	z_formats.push_back( D3DFMT_D24S8 );
//	if( parameters->shadow_passes == 0 )
	{
		z_formats.push_back( D3DFMT_D24X4S4 );
		z_formats.push_back( D3DFMT_D15S1 );
	}

	parameters->render_target_format = D3DFMT_UNKNOWN;
	parameters->z_buffer_format = D3DFMT_UNKNOWN;
	for( int rtf = 0; rtf != rt_formats.size() && parameters->render_target_format == D3DFMT_UNKNOWN; ++rtf )
	{
		if( direct3d->CheckDeviceFormat( adapterInfo.AdapterNumber, adapterInfo.DeviceType,
				parameters->display_format, D3DUSAGE_RENDERTARGET, 
				D3DRTYPE_TEXTURE, rt_formats[rtf] ) != D3D_OK )
		{
			continue;
		}
/*		if( parameters->need_cubic_rendertarget )
		{
			if( direct3d->CheckDeviceFormat( adapter_number, D3DDEVTYPE_HAL,
					parameters->display_format, D3DUSAGE_RENDERTARGET, 
					D3DRTYPE_CUBETEXTURE, rt_formats[rtf] ) != D3D_OK )
			{
				continue;
			}
		}
		*/
		for( int zf = 0; zf != z_formats.size() && parameters->z_buffer_format == D3DFMT_UNKNOWN; ++zf )
		{
			if( direct3d->CheckDepthStencilMatch( adapterInfo.AdapterNumber, adapterInfo.DeviceType,
				parameters->display_format, parameters->color_format, z_formats[zf] ) == D3D_OK &&
				direct3d->CheckDepthStencilMatch( adapterInfo.AdapterNumber, adapterInfo.DeviceType,
				parameters->display_format, rt_formats[rtf], z_formats[zf] ) == D3D_OK )
			{
				parameters->render_target_format = rt_formats[rtf];
				parameters->z_buffer_format = z_formats[zf];
			}
		}
	}
	if( parameters->render_target_format == D3DFMT_UNKNOWN )
	{
		for( int zf = 0; zf != z_formats.size() && parameters->z_buffer_format == D3DFMT_UNKNOWN; ++zf )
		{
			if( direct3d->CheckDepthStencilMatch( adapterInfo.AdapterNumber, adapterInfo.DeviceType,
				parameters->display_format, parameters->color_format, z_formats[zf] ) == D3D_OK )
			{
				parameters->z_buffer_format = z_formats[zf];
			}
		}
	}
	if( parameters->z_buffer_format == D3DFMT_UNKNOWN )
		return false;

	for( int ff = 0; ff != pictureformat::FORMAT_SIZE; ++ff)
	{
		//NOTE: this is VERY dependent on the items of the pictureformat::Format
		//If any item is missing, hardware will be inoperable
		std::vector<D3DFORMAT> tex_formats;
		pictureformat::Format pf = pictureformat::Format(ff);
		switch( pf )
		{
		case pictureformat::R8G8B8A8:
		case pictureformat::A8R8G8B8:
			if( prefer_16bit_textures )
			{
				tex_formats.push_back( D3DFMT_A4R4G4B4 );
				tex_formats.push_back( D3DFMT_A8R8G8B8 );
				tex_formats.push_back( D3DFMT_A1R5G5B5 );
			}
			else
			{
				tex_formats.push_back( D3DFMT_A8R8G8B8 );
				tex_formats.push_back( D3DFMT_A4R4G4B4 );
				tex_formats.push_back( D3DFMT_A1R5G5B5 );
			}
			break;
		case pictureformat::A1R5G5B5:
			tex_formats.push_back( D3DFMT_A1R5G5B5 );
			if( prefer_16bit_textures )
			{
				tex_formats.push_back( D3DFMT_A4R4G4B4 );
				tex_formats.push_back( D3DFMT_A8R8G8B8 );
			}
			else
			{
				tex_formats.push_back( D3DFMT_A8R8G8B8 );
				tex_formats.push_back( D3DFMT_A4R4G4B4 );
			}
			break;
		case pictureformat::A4R4G4B4:
			tex_formats.push_back( D3DFMT_A4R4G4B4 );
			tex_formats.push_back( D3DFMT_A8R8G8B8 );
			tex_formats.push_back( D3DFMT_A1R5G5B5 );
			break;
		case pictureformat::R8G8B8:
		case pictureformat::B8G8R8: // bad format :)
		case pictureformat::R8G8B8CK:
			if( prefer_16bit_textures )
			{
				tex_formats.push_back( D3DFMT_R5G6B5 );
				tex_formats.push_back( D3DFMT_A1R5G5B5 );
				tex_formats.push_back( D3DFMT_X1R5G5B5 );
				tex_formats.push_back( D3DFMT_A4R4G4B4 );
				tex_formats.push_back( D3DFMT_R8G8B8 );
				tex_formats.push_back( D3DFMT_X8R8G8B8 );
				tex_formats.push_back( D3DFMT_A8R8G8B8 );
			}
			else
			{
				tex_formats.push_back( D3DFMT_R8G8B8 );
				tex_formats.push_back( D3DFMT_X8R8G8B8 );
				tex_formats.push_back( D3DFMT_A8R8G8B8 );
				tex_formats.push_back( D3DFMT_R5G6B5 );
				tex_formats.push_back( D3DFMT_X1R5G5B5 );
				tex_formats.push_back( D3DFMT_A1R5G5B5 );
				tex_formats.push_back( D3DFMT_A4R4G4B4 );
			}
			break;
		case pictureformat::R5G6B5:
		case pictureformat::R5G6B5CK:
			if( prefer_16bit_textures )
			{
				tex_formats.push_back( D3DFMT_R5G6B5 );
				tex_formats.push_back( D3DFMT_X1R5G5B5 );
				tex_formats.push_back( D3DFMT_A1R5G5B5 );
				tex_formats.push_back( D3DFMT_A4R4G4B4 );
				tex_formats.push_back( D3DFMT_R8G8B8 );
				tex_formats.push_back( D3DFMT_X8R8G8B8 );
				tex_formats.push_back( D3DFMT_A8R8G8B8 );
			}
			else
			{
				tex_formats.push_back( D3DFMT_R5G6B5 );
				tex_formats.push_back( D3DFMT_X1R5G5B5 );
				tex_formats.push_back( D3DFMT_A1R5G5B5 );
				tex_formats.push_back( D3DFMT_R8G8B8 );
				tex_formats.push_back( D3DFMT_X8R8G8B8 );
				tex_formats.push_back( D3DFMT_A8R8G8B8 );
				tex_formats.push_back( D3DFMT_A4R4G4B4 );
			}
			break;
		}
		int tf = 0;
		for( ; tf != tex_formats.size(); ++tf )
		{
			if( direct3d->CheckDeviceFormat( adapterInfo.AdapterNumber, adapterInfo.DeviceType,
					parameters->display_format, 0, 
					D3DRTYPE_TEXTURE, tex_formats[tf] ) == D3D_OK )
			{
				break;
			}
		}
		if( tf == tex_formats.size() )
			return false;
		parameters->format_for_texture[pf] = tex_formats[tf];

		for( tf = 0; tf != tex_formats.size(); ++tf )
		{
			if( direct3d->CheckDeviceFormat( adapterInfo.AdapterNumber, adapterInfo.DeviceType,
					parameters->display_format, 0, 
					D3DRTYPE_CUBETEXTURE, tex_formats[tf] ) == D3D_OK )
			{
				break;
			}
		}
		if( tf == tex_formats.size() )
			parameters->format_for_cubic_texture[pf] = D3DFMT_UNKNOWN;
		else
			parameters->format_for_cubic_texture[pf] = tex_formats[tf];
	}

	return true;
}

Hardware::Hardware( LPDIRECT3D9 direct3d, AdapterInfo adapterInfo, std::map<std::string,int> & caps_map )
:	direct3d( direct3d ),
	device( 0 ),
	window_styles( 0 ),
	device_objects( 0 ),
	tables( 0 ),
	adapterInfo( adapterInfo ),
	curr_pos( 0 ),
	old_pos( 0 ),
	current_render_list( NULL ),
	current_blend_mode(BM_UNINITIALIZED)
{
	curr_mat[ curr_pos ] = Matrix::identity();

	memset( &present_parameters, 0, sizeof(D3DPRESENT_PARAMETERS) );

	D3DADAPTER_IDENTIFIER9 adapter_identifier;
	direct3d->GetAdapterIdentifier( adapterInfo.AdapterNumber, 0, &adapter_identifier );

	hardware_name = adapter_identifier.Description;
	// + 1 for user's pleasure to count from 1
	{
		std::stringstream ss;
		ss << adapterInfo.AdapterNumber + 1 << ": DirectX8 on " << hardware_name;
		device_name = ss.str();
	}

	max_anisotropy = caps_map["device_caps.MaxAnisotropy"];
	can_render_sprites = caps_map["device_caps.MaxPointSize"] > 1;
	max_vertex_index = caps_map["device_caps.MaxVertexIndex"];
	can_render_mipmaps = caps_map["device_caps.Caps2.D3DCAPS2_CANAUTOGENMIPMAP"];
	can_render_shadows = caps_map["device_caps.PrimitiveMiscCaps.D3DPMISCCAPS_COLORWRITEENABLE"] &&
		(caps_map["device_caps.StencilCaps.D3DSTENCILCAPS_DECRSAT"] || caps_map["device_caps.StencilCaps.D3DSTENCILCAPS_DECR"]) &&
		(caps_map["device_caps.StencilCaps.D3DSTENCILCAPS_INCRSAT"] || caps_map["device_caps.StencilCaps.D3DSTENCILCAPS_INCR"]) &&
		caps_map["device_caps.StencilCaps.D3DSTENCILCAPS_KEEP"];
	
	stencil_incop = caps_map["device_caps.StencilCaps.D3DSTENCILCAPS_INCRSAT"] ? D3DSTENCILOP_INCRSAT : D3DSTENCILOP_INCR;
	stencil_decop = caps_map["device_caps.StencilCaps.D3DSTENCILCAPS_DECRSAT"] ? D3DSTENCILOP_DECRSAT : D3DSTENCILOP_DECR;

	can_render_alpha = caps_map["device_caps.AlphaCmpCaps.D3DPCMPCAPS_EQUAL"] &&
		caps_map["device_caps.AlphaCmpCaps.D3DPCMPCAPS_NOTEQUAL"] &&
		caps_map["device_caps.TextureCaps.D3DPTEXTURECAPS_ALPHA"];
	use_hardware_vshaders = caps_map["device_caps.VertexShaderVersion"] >= 0x101;
	try_hardware_vp = caps_map["device_caps.DevCaps.D3DDEVCAPS_HWTRANSFORMANDLIGHT"];

	max_texture_size = std::min( caps_map["device_caps.MaxTextureWidth"], caps_map["device_caps.MaxTextureHeight"] );

	logs::log() << "\nSummary for device: " << device_name << "\n";
	logs::log() << "--------------------------------------------------------------\n";
	logs::log() << (can_render_sprites ? "Sprites\n" : "No sprites\n");
	logs::log() << (can_render_mipmaps ? "Auto Mipmaps\n" : "No auto mipmaps\n");
	logs::log() << (can_render_shadows ? "Shadows\n" : "No shadows\n");
	logs::log() << (can_render_alpha ? "Transparency\n" : "No transparency\n");
	logs::log() << (max_anisotropy ? "Anisotropic filtering\n" : "No anisotropic filtering\n");
	logs::log() << (use_hardware_vshaders ? "Try Using Hardware Vertex Shader\n" : "Not Using Hardware Vertex Shaders\n");
	logs::log() << (try_hardware_vp ? "Try Hardware Vertex Processing\n" : "Skip hardware vertex processing\n");

	std::string haloname;
	for( int i = 0; i != hardware_name.size(); ++i )
		haloname += tolower( hardware_name[i] );
	bool intel_found = (haloname.find("intel") != std::string::npos);
	bool geforce_found = (haloname.find("geforce") != std::string::npos);
	if( intel_found )
		logs::log() << "ByName detection: Intel found\n";
	if( geforce_found )
		logs::log() << "ByName detection: GeForce found\n";

	if( caps_map["device_caps.PixelShaderVersion"] >= 0x104 && 
		caps_map["device_caps.MaxSimultaneousTextures"] >= 6 && !geforce_found )
	{
		logs::log() << "Using Radeon8500-compatible driver\n";
		style = PS00;
		prefer_16bit_textures = false;
		parameters.resolution_x = 1024;
		parameters.resolution_y = 768;
//		parameters.shadow_passes = 1;
//		parameters.lighting_passes = 8;

/*		style = PS14;
		prefer_16bit_textures = false;
		parameters.shadow_passes = 1;
		parameters.lighting_passes = 8;
		parameters.resolution_x = 800;
		parameters.resolution_y = 600;
		*/
	}
	else
	if( caps_map["device_caps.PixelShaderVersion"] >= 0x103 && 
		caps_map["device_caps.MaxSimultaneousTextures"] >= 4 )
	{
		logs::log() << "Using GeForce4-compatible driver\n";
		style = PS00;
		prefer_16bit_textures = false;
		parameters.resolution_x = 1024;
		parameters.resolution_y = 768;
//		parameters.shadow_passes = 1;
//		parameters.lighting_passes = 8;

/*		style = PS13;
		prefer_16bit_textures = false;
		parameters.shadow_passes = 1;
		parameters.lighting_passes = 8;
		parameters.resolution_x = 800;
		parameters.resolution_y = 600;
		*/
	}
	else
	{
//		parameters.lighting_passes = 0;
		prefer_16bit_textures = true;
		if( try_hardware_vp ) // GeForce4 MX
		{
			logs::log() << "Using Old-Style driver on fast card, like GeForce4MX\n";
			style = PS00;
			parameters.resolution_x = 1024;
			parameters.resolution_y = 768;
//			parameters.shadow_passes = 1;
//			parameters.lighting_passes = 8;
		}
		else
		{
			if( caps_map["device_caps.TextureOpCaps.D3DTEXOPCAPS_MULTIPLYADD"] && !intel_found )
			{
				logs::log() << "Using Old-Style driver on moderate card, like GeForce2\n";
				style = PS00;
				parameters.resolution_x = 1024;
				parameters.resolution_y = 768;
//				parameters.shadow_passes = 1;
//				parameters.lighting_passes = 8;
			}
			else
			{
				logs::log() << "Using Old-Style driver on very old card (or on Intel card :))\n";
				style = SLOW;
				parameters.resolution_x = 800;//512;
				parameters.resolution_y = 600;//384;
//				parameters.shadow_passes = 1;
//				parameters.lighting_passes = 8;
			}
		}
	}
///	style = PS00;
///	prefer_16bit_textures = true;

	std::stringstream ss;
	Strings names;
//	names.push_back("NeedCubicRenderTarget");
//	names.push_back("Need8RTColorChannelBits");
//	names.push_back("ShadowPasses");
	names.push_back("Fullscreen");
	names.push_back("ResolutionX");
	names.push_back("ResolutionY");
//	names.push_back("LightingPasses");
	names.push_back("Antialiasing");
	Strings values;
//	values.push_back("false");
//	values.push_back("false");
//	values.push_back(String() << parameters.shadow_passes);
	values.push_back("Yes");
	ss.clear();
	ss << parameters.resolution_x;
	values.push_back(ss.str());
	ss.clear();
	ss << parameters.resolution_y;
	values.push_back(ss.str());
//	values.push_back(String() << parameters.lighting_passes);
	//todo anti
	values.push_back("None");

	set_attribute_names( names, values );

	// Fill parameters tables
/*	parameters.need_cubic_rendertarget = false;
	do_need_cubic_rendertarget(&parameters);
	parameters.need_cubic_rendertarget = true;
	do_need_cubic_rendertarget(&parameters);
	*/

	parameters.is_fullscreen = false;
	do_is_fullscreen( &parameters );
	parameters.is_fullscreen = true;
	do_is_fullscreen( &parameters );
}

void Hardware::do_need_cubic_rendertarget(Parameters * parameters)
{
/*	parameters->need_8_rt_color_channel_bits = false;
	do_need_8_rt_color_channel_bits(parameters);
	parameters->need_8_rt_color_channel_bits = true;
	do_need_8_rt_color_channel_bits(parameters);
	*/
}

void Hardware::do_need_8_rt_color_channel_bits(Parameters * parameters)
{
/*	parameters->shadow_passes = 0;
	do_shadow_passes(parameters);
	if( style != SLOW )
	{
		parameters->shadow_passes = 1;
		do_shadow_passes(parameters);
		parameters->shadow_passes = 2;
		do_shadow_passes(parameters);
		parameters->shadow_passes = 4;
		do_shadow_passes(parameters);
		parameters->shadow_passes = 8;
		do_shadow_passes(parameters);
	}
	*/
}

void Hardware::do_shadow_passes(Parameters * parameters)
{
	/*
	parameters->is_fullscreen = false;
	do_is_fullscreen(parameters);
	parameters->is_fullscreen = true;
	do_is_fullscreen(parameters);
	*/
}

void Hardware::do_is_fullscreen(Parameters * parameters)
{
	if( !fill_dependent_parameters(parameters) )
		return;
	std::vector< std::pair<int,int> > result;

	if( parameters->is_fullscreen )
	{
		int num = direct3d->GetAdapterModeCount( adapterInfo.AdapterNumber, D3DFMT_UNKNOWN );
		for( int i = 0; i < num; ++i )
		{
			D3DDISPLAYMODE mode;
			if( direct3d->EnumAdapterModes( adapterInfo.AdapterNumber, D3DFMT_UNKNOWN, i, &mode ) == D3D_OK )
			{
				result.push_back( std::pair<int,int>(mode.Width, mode.Height) );
			}
		}
	}
	else
	{
		result.push_back( std::pair<int,int>(0, 0) );
	}
	std::sort( result.begin(), result.end() );
	result.erase( std::unique( result.begin(), result.end() ), result.end() );
	Strings table_row( 4 );
//	converter::convert( parameters->need_cubic_rendertarget, &table_row[0] );
//	converter::convert( parameters->need_8_rt_color_channel_bits, &table_row[1] );
//	converter::convert( parameters->shadow_passes, &table_row[2] );

	if( !parameters->is_fullscreen )
		table_row[0] = WINDOWED_NAME;
	else
		table_row[0] = "Yes";

//	converter::convert( parameters->is_fullscreen, &table_row[0] );
	for( int i = 0; i != result.size(); ++i )
	{
		converter::convert( result[i].first, &table_row[1] );
		converter::convert( result[i].second, &table_row[2] );

		table_row[3] = ANTIALIASING_NONE_NAME;
		add_attribute_table_row( table_row );
		if( direct3d->CheckDeviceMultiSampleType( adapterInfo.AdapterNumber, adapterInfo.DeviceType, parameters->color_format, !parameters->is_fullscreen, D3DMULTISAMPLE_2_SAMPLES, 0 ) == D3D_OK)
		if( direct3d->CheckDeviceMultiSampleType( adapterInfo.AdapterNumber, adapterInfo.DeviceType, parameters->z_buffer_format, !parameters->is_fullscreen, D3DMULTISAMPLE_2_SAMPLES, 0 ) == D3D_OK)
		{
			table_row[3] = ANTIALIASING_2X_NAME;
			add_attribute_table_row( table_row );
		}

		if( direct3d->CheckDeviceMultiSampleType( adapterInfo.AdapterNumber, adapterInfo.DeviceType, parameters->color_format, !parameters->is_fullscreen, D3DMULTISAMPLE_4_SAMPLES, 0 ) == D3D_OK)
		if( direct3d->CheckDeviceMultiSampleType( adapterInfo.AdapterNumber, adapterInfo.DeviceType, parameters->z_buffer_format, !parameters->is_fullscreen, D3DMULTISAMPLE_4_SAMPLES, 0 ) == D3D_OK)
		{
			table_row[3] = ANTIALIASING_4X_NAME;
			add_attribute_table_row( table_row );
		}

/*		table_row[6] = "0";
		add_attribute_table_row( table_row );
		if( style != SLOW )
		{
			table_row[6] = "1";
			add_attribute_table_row( table_row );
			table_row[6] = "2";
			add_attribute_table_row( table_row );
			table_row[6] = "4";
			add_attribute_table_row( table_row );
			table_row[6] = "8";
			add_attribute_table_row( table_row );
		}
		*/
	}
}

Hardware::~Hardware()
{
	stop_mode();
}

std::string Hardware::get_hardware_name()
{
	return hardware_name;
}

std::string Hardware::get_name()
{
	return device_name;
}

D3DFORMAT Hardware::remap_pictureformat(const pictureformat::Format & desired_format)
{
	static D3DFORMAT PictureFormatRemapper[]
	=
	{
		D3DFMT_A8R8G8B8, //32
		D3DFMT_R8G8B8, D3DFMT_A8R8G8B8, D3DFMT_R8G8B8, // 24
		D3DFMT_R5G6B5, D3DFMT_A1R5G5B5, D3DFMT_A4R4G4B4, D3DFMT_A1R5G5B5 // 16
	};
	return PictureFormatRemapper[desired_format];
}

pictureformat::Format Hardware::remap_pictureformat(D3DFORMAT desired_format)
{
	switch(desired_format)
	{
	case D3DFMT_A8R8G8B8:
		return pictureformat::A8R8G8B8;
	case D3DFMT_R5G6B5:
		return pictureformat::R5G6B5;
	case D3DFMT_A1R5G5B5:
		return pictureformat::A1R5G5B5;
	case D3DFMT_X1R5G5B5:
		return pictureformat::A1R5G5B5;
	case D3DFMT_A4R4G4B4:
		return pictureformat::A4R4G4B4;
	case D3DFMT_R8G8B8:
		return pictureformat::R8G8B8;
	case D3DFMT_X8R8G8B8:
		return pictureformat::A8R8G8B8;
	}
	throw Gluck(__FUNCTION__,"D3DFORMAT could not be represented by pictureformat::Format");
}

void Hardware::recreate_z_buffer_surface()
{
	if( !device_objects )
		return;
	int wi = 0;
	int he = 0;
	RenderTargets::const_iterator it = render_targets.begin();
	for( ; it != render_targets.end(); ++it )
	{
		wi = std::max( wi, (*it)->get_width() );
		he = std::max( he, (*it)->get_height() );
	}
	device_objects->recreate_z_buffer_surface( wi, he, parameters.z_buffer_format );
}

bool Hardware::set_mode_internal(WindowImpl * window, const Strings & attribute_values)
{
//	if( !converter::convert( attribute_values[0], &parameters.need_cubic_rendertarget ) )
//		return false;
//	if( !converter::convert( attribute_values[1], &parameters.need_8_rt_color_channel_bits ) )
//		return false;
//	if( !converter::convert( attribute_values[2], &parameters.shadow_passes ) )
//		return false;
//	if( !converter::convert( attribute_values[6], &parameters.lighting_passes ) )
//		return false;

	if( attribute_values[0] == WINDOWED_NAME )
		parameters.is_fullscreen = false;
	else
		parameters.is_fullscreen = true;

	//MaxK
	parameters.is_fullscreen = false;

//	if( !converter::convert( attribute_values[0], &parameters.is_fullscreen ) )
//		return false;
	if( !converter::convert( attribute_values[1], &parameters.resolution_x ) )
		return false;
	if( !converter::convert( attribute_values[2], &parameters.resolution_y ) )
		return false;

	if( attribute_values[3] == ANTIALIASING_2X_NAME )
		parameters.multisample_type = D3DMULTISAMPLE_2_SAMPLES;
	else
	if( attribute_values[3] == ANTIALIASING_4X_NAME )
		parameters.multisample_type = D3DMULTISAMPLE_4_SAMPLES;
	else
		parameters.multisample_type = D3DMULTISAMPLE_4_SAMPLES;

	if(direct3d->CheckDeviceMultiSampleType(adapterInfo.AdapterNumber, adapterInfo.DeviceType, parameters.color_format, !parameters.is_fullscreen, parameters.multisample_type, 0) != D3D_OK ||
		direct3d->CheckDeviceMultiSampleType(adapterInfo.AdapterNumber, adapterInfo.DeviceType, parameters.z_buffer_format, !parameters.is_fullscreen, parameters.multisample_type, 0) != D3D_OK)
	{
		parameters.multisample_type = D3DMULTISAMPLE_NONE;
	}

//	parameters.is_fullscreen = false;
	bool fill_result = fill_dependent_parameters(&parameters);

	logs::log() << "color_format: " << parameters.color_format << "\n";
	logs::log() << "display_format: " << parameters.display_format << "\n";
	logs::log() << "format_for_texture:\n";
	for( int ff = 0; ff != pictureformat::FORMAT_SIZE; ++ff)
		logs::log() << ff << ": " << parameters.format_for_texture[ff] << "\n";
	logs::log() << "format_for_cubic_texture:\n";
	for( int ff = 0; ff != pictureformat::FORMAT_SIZE; ++ff)
		logs::log() << ff << ": " << parameters.format_for_cubic_texture[ff] << "\n";
	logs::log() << "is_fullscreen: " << parameters.is_fullscreen << "\n";
//	logs::log() << "lighting_passes: " << parameters.lighting_passes << "\n";
//	logs::log() << "need_8_rt_color_channel_bits: " << parameters.need_8_rt_color_channel_bits << "\n";
//	logs::log() << "need_cubic_rendertarget: " << parameters.need_cubic_rendertarget << "\n";
	logs::log() << "render_target_format: " << parameters.render_target_format << "\n";
	logs::log() << "resolution_x: " << parameters.resolution_x << "\n";
	logs::log() << "resolution_y: " << parameters.resolution_y << "\n";
//	logs::log() << "shadow_passes: " << parameters.shadow_passes << "\n";
	logs::log() << "z_buffer_format: " << parameters.z_buffer_format << "\n";
	if( !fill_result )
		return false;
	logs::log() << "Hardware::set_mode_internal() dependent parameters are ok\n";

	memset( &present_parameters, 0, sizeof(D3DPRESENT_PARAMETERS) );

	present_parameters.SwapEffect = D3DSWAPEFFECT_DISCARD;//D3DSWAPEFFECT_COPY_VSYNC;//D3DSWAPEFFECT_FLIP;
	present_parameters.BackBufferFormat = parameters.color_format;
///	present_parameters.Flags = D3DPRESENTFLAG_DISCARD_DEPTHSTENCIL;
	present_parameters.BackBufferCount = 1;
	present_parameters.hDeviceWindow = window->get_wnd();
	present_parameters.MultiSampleType = parameters.multisample_type;

	present_parameters.EnableAutoDepthStencil = true;
	present_parameters.AutoDepthStencilFormat = parameters.z_buffer_format;

	window_styles = GetWindowLong( present_parameters.hDeviceWindow, GWL_STYLE );
	if( parameters.is_fullscreen )
	{
		present_parameters.PresentationInterval = D3DPRESENT_INTERVAL_ONE; //D3DPRESENT_INTERVAL_IMMEDIATE;
		present_parameters.Windowed = false;
		present_parameters.BackBufferWidth = parameters.resolution_x;
		present_parameters.BackBufferHeight = parameters.resolution_y;
		MoveWindow( present_parameters.hDeviceWindow, 0, 0, present_parameters.BackBufferWidth, present_parameters.BackBufferHeight, true );
		SetWindowLong( present_parameters.hDeviceWindow, GWL_STYLE, WS_POPUP );
	}
	else
	{
/*		int width = parameters.resolution_x;
		int height = parameters.resolution_y;
		RECT win_rect;
		GetWindowRect( present_parameters.hDeviceWindow, &win_rect );
		RECT rect;
		GetClientRect( present_parameters.hDeviceWindow, &rect );
		int new_width = width +	(win_rect.right - win_rect.left) - (rect.right - rect.left);
		int new_height = height + (win_rect.bottom - win_rect.top) - (rect.bottom - rect.top);

		//centering window
		int scr_x = GetSystemMetrics( SM_CXSCREEN );
		int scr_y = GetSystemMetrics( SM_CYSCREEN );
		int shi_x = (scr_x - new_width)/2;
		int shi_y = (scr_y - new_height)/2;
		if( shi_x < 0 )	shi_x = 0;
		if( shi_y < 0 )	shi_y = 0;
		MoveWindow( present_parameters.hDeviceWindow, shi_x, shi_y, new_width, new_height, true );
*/
		present_parameters.Windowed = true;
		present_parameters.BackBufferWidth = 0;
		present_parameters.BackBufferHeight = 0;
	}
	SetWindowPos( present_parameters.hDeviceWindow, 0, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED );
	GetClientRect( present_parameters.hDeviceWindow, &current_rect );

//	try_hardware_vp = false;
	if( try_hardware_vp )
	{
		if( !device )
		{
			DWORD behavior_flag = D3DCREATE_HARDWARE_VERTEXPROCESSING;
			if( !use_hardware_vshaders )
				behavior_flag = D3DCREATE_MIXED_VERTEXPROCESSING;

			logs::log() << "Hardware::set_mode_internal() try_hardware_vp\n";
			present_parameters.Flags = 0;
			hr = direct3d->CreateDevice( adapterInfo.AdapterNumber, adapterInfo.DeviceType, present_parameters.hDeviceWindow, 
							behavior_flag /*| D3DCREATE_MULTITHREADED*/, &present_parameters, &device );
		}
	}
	if( !device )
	{
		if( !device )
		{
			logs::log() << "Hardware::set_mode_internal() try_software_vp\n";
			present_parameters.Flags = 0;
			hr = direct3d->CreateDevice( adapterInfo.AdapterNumber, adapterInfo.DeviceType, present_parameters.hDeviceWindow, 
								D3DCREATE_SOFTWARE_VERTEXPROCESSING,
								&present_parameters, &device );

			try_hardware_vp = false;
			use_hardware_vshaders = false;
		}
		if( !device )
		{
			logs::log() << logs::error << "Hardware::set_mode_internal() CreateDevice failed\n";
			return false;
		}
	}
	device_memory = 0;
	report_device_memory();
	restore_resources();
	report_device_memory();

	logs::log() << "Mode set\n";

	return true;
}

void Hardware::report_device_memory()
{
	int new_device_memory = device->GetAvailableTextureMem();
	if( device_memory != new_device_memory )
	{
		device_memory = new_device_memory;
		logs::log() << "Device (Video+AGP) Memory Available: " << double(device_memory)/1024/1024 << "Mb\n";
	}
}

void Hardware::lose_resources()
{
	logs::log() << "Hardware::lose_resources()\n";
	device_objects.reset();
	tables.reset();

	if( device )
	{
		for( int i = 0; i < MAX_TEXTURES; ++i )
		{
			device->SetTexture( i, 0 );
		}
		device->SetStreamSource( 0, 0, 0, 0 );
		device->SetStreamSource( 1, 0, 0, 0 );
		device->SetIndices(0);
	}

	Dependent::List::iterator dep_it = dependent_list.begin();
	for( ; dep_it != dependent_list.end(); ++dep_it )
		(*dep_it)->invalidate();

	RenderTargets::iterator rt_it = render_targets.begin();
	for( ; rt_it != render_targets.end(); ++rt_it )
		(*rt_it)->release();
}

void Hardware::restore_resources()
{
	logs::log() << "Hardware::restore_resources()\n";
	device_objects.reset(new DeviceObjects(device, style, use_hardware_vshaders, parameters.render_target_format));
	tables.reset(new Tables(device, style, parameters.format_for_texture[pictureformat::A8R8G8B8]));
	recreate_z_buffer_surface();

	device->SetSamplerState( 0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR );
	device->SetSamplerState( 0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR );
	device->SetSamplerState( 0, D3DSAMP_MIPFILTER, D3DTEXF_LINEAR );
	device->SetSamplerState( 1, D3DSAMP_MINFILTER, D3DTEXF_LINEAR );
	device->SetSamplerState( 1, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR );
	device->SetSamplerState( 1, D3DSAMP_MIPFILTER, D3DTEXF_LINEAR );

	//device->SetRenderState( D3DRS_MULTISAMPLEANTIALIAS, TRUE );
	device->SetRenderState( D3DRS_ALPHABLENDENABLE, TRUE );
	device->SetRenderState( D3DRS_SPECULARENABLE, FALSE );//TRUE );

	//HERE
//	device->SetRenderState( D3DRS_FILLMODE, D3DFILL_WIREFRAME );

//	Dependent::List::iterator dep_it = dependent_list.begin();
//	for( ; dep_it != dependent_list.end(); ++dep_it )
//		(*dep_it)->invalidate();
}

void Hardware::stop_mode()
{
	logs::log() << "Hardware::stop_mode()\n";
	lose_resources();
	if( device )
	{
		GlobalRelease( &device );
	}
	SetWindowLong( present_parameters.hDeviceWindow, GWL_STYLE, window_styles );
	SetWindowPos( present_parameters.hDeviceWindow, 0, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED );
}

hard::Texture * Hardware::create_texture(int mipmap_levels, const pictureformat::Format & desired_format, int width, int height)
{
	D3DFORMAT haf = parameters.format_for_texture[desired_format];
	return new Texture( this, mipmap_levels, remap_pictureformat(haf), haf, width, height, max_texture_size );
}

class TextureCubicNull : public hard::TextureCubic
{
public:
	explicit TextureCubicNull(int mipmap_levels, const pictureformat::Format & desired_format, int width, int height):
		hard::TextureCubic( mipmap_levels, desired_format, width, height )
	{}
	virtual void * lock(int mipmap_level, int plane, int * stride)
	{
		return 0;
	}
	virtual void unlock(int mipmap_level, int plane)
	{}

	virtual bool is_valid()
	{
		return true;
	}
	virtual bool set_in_hardware(int stage)
	{
		return true;
	}
};

hard::TextureCubic * Hardware::create_texture_cubic(int mipmap_levels, const pictureformat::Format & desired_format, int width, int height)
{
	D3DFORMAT haf = parameters.format_for_cubic_texture[desired_format];
	if( haf == D3DFMT_UNKNOWN )
		return new TextureCubicNull( mipmap_levels, pictureformat::A8R8G8B8, width, height );
	return new TextureCubic( this, mipmap_levels, remap_pictureformat(haf), haf, width, height );
}

hard::VertexBuffer * Hardware::create_vertex_buffer( bool dynamic )
{
	return new VertexBuffer( this, dynamic );
}

hard::IndexBuffer * Hardware::create_index_buffer()
{
	return new IndexBuffer( this, max_vertex_index );
}

hard::PointBuffer * Hardware::create_point_buffer()
{
	return new PointBuffer( this );
}

hard::RenderTarget * Hardware::create_render_target(const Atom & render_target_name, int width, int height)
{
	directx8::RenderTarget * rt = new directx8::RenderTarget( this, render_target_name, width, height );
	render_targets.push_back( rt );
	recreate_z_buffer_surface();
	return rt;
}

hard::TextureBase * Hardware::get_render_target(const Atom & render_target_name)
{
	RenderTargets::iterator it = render_targets.begin();
	for( ;it != render_targets.end(); ++it )
		if( (*it)->get_render_target_name() == render_target_name )
			return *it;
	return 0;
}

void Hardware::set_fog( const Color & color, bool enable )
{
	float density = 0.66f;  // For exponential modes
	float start = 0.0f;    // For linear mode
	float end   = 60;

	device->SetRenderState(D3DRS_FOGENABLE, enable);
    device->SetRenderState(D3DRS_FOGCOLOR, get_dword_color( color, 1 ) );
	device->SetRenderState(D3DRS_FOGVERTEXMODE, D3DFOG_LINEAR);
//	device->SetRenderState(D3DRS_FOGDENSITY, *(DWORD *)(&density) );

	device->SetRenderState(D3DRS_FOGSTART, *(DWORD *)(&start));
	device->SetRenderState(D3DRS_FOGEND,   *(DWORD *)(&end));
}

void Hardware::set_texture_alt( int stage, TextureBase * tex, IDirect3DBaseTexture9 * alt_tex )
{
	if( tex )
		tex->set_in_hardware( stage );
	else
			device->SetTexture( stage, alt_tex );
}

void Hardware::set_params_for_start_scene(int left, int right, int top, int bottom)
{
	viewport.X = left;
	viewport.Y = top;
	viewport.Width = right - left;
	viewport.Height = bottom - top;
	viewport.MinZ = 0.0f;
	viewport.MaxZ = 1.0f;

	device->SetViewport( &viewport );

	Matrix matWorld = Matrix::identity();
	Matrix matView = Matrix::identity();
	Matrix matProj = Matrix::identity();
	device->SetTransform( D3DTS_PROJECTION, (D3DMATRIX *)&matProj );
	device->SetTransform( D3DTS_VIEW, (D3DMATRIX *)&matView );
	device->SetTransform( D3DTS_WORLD, (D3DMATRIX *)&matWorld );

	hr = device->SetRenderState(D3DRS_NORMALIZENORMALS, TRUE);
///	hr = device->SetRenderState(D3DRS_LOCALVIEWER, TRUE);

	device->SetRenderState( D3DRS_ZENABLE, TRUE );
	device->SetRenderState( D3DRS_STENCILENABLE, FALSE );

	device->SetRenderState( D3DRS_DITHERENABLE, FALSE );
	device->SetRenderState( D3DRS_POINTSPRITEENABLE, TRUE );
}

bool Hardware::start_scene(double left, double right, double top, double bottom)
{
	hr = device->SetRenderTarget( 0, device_objects->render_target_default );
	hr = device->SetDepthStencilSurface( device_objects->z_buffer_surface );

	hr = device->BeginScene();
	if( FAILED( hr ) )
	{
		logs::log() << logs::warning << "BeginScene() failed\n";
		return false;
	}

	int le = current_rect.left + get_width() * (left + 1)/2;
	int ri = current_rect.left + get_width() * (right + 1)/2;
	int to = current_rect.bottom - get_height() * (top + 1)/2;
	int bo = current_rect.bottom - get_height() * (bottom + 1)/2;
	set_params_for_start_scene( le, ri, to, bo );

	return true;
}

const int Hardware::get_width()const
{
	return current_rect.right - current_rect.left;
}

const int Hardware::get_height()const
{
	return current_rect.bottom - current_rect.top;
}

void Hardware::finish_scene()
{
	device->EndScene();
}

bool Hardware::start_frame()
{
	RECT new_rect;
	GetClientRect( present_parameters.hDeviceWindow, &new_rect );

	if( new_rect.bottom - new_rect.top == 0 || new_rect.right - new_rect.left == 0 )
	{
		logs::log() << "Hardware::start_frame() : Cannot render in minimized mode\n";
		return false;
	}
	HRESULT device_state = device->TestCooperativeLevel();
	if( device_state == D3DERR_DEVICELOST )
	{
		// free everything...
		logs::log() << "Hardware::start_frame() : D3DERR_DEVICELOST\n";
		if( is_operational() )
			lose_resources();
		//device->EvictManagedResources();
		return false;
	}
	if( device_state == D3DERR_DEVICENOTRESET )
	{
		logs::log() << "Hardware::start_frame() : D3DERR_DEVICENOTRESET\n";
		if( is_operational() )
			lose_resources();
		present_parameters.BackBufferCount = 1;
		if( !parameters.is_fullscreen )
		{
			present_parameters.BackBufferWidth = 0;
			present_parameters.BackBufferHeight = 0;
		}
		hr = device->Reset( &present_parameters );
		if( FAILED( hr ) )
		{
			logs::log() << logs::warning << "Hardware::start_frame(): device->Reset() failed hr=" << hr << "\n";
			return false;
		}
		restore_resources();
		return false;
	}
	if( current_rect.bottom - current_rect.top != new_rect.bottom - new_rect.top ||
		current_rect.right - current_rect.left != new_rect.right - new_rect.left )
	{
		logs::log() << "Hardware::start_frame() : window size changed\n";
		if( is_operational() )
			lose_resources();
		present_parameters.BackBufferCount = 1;
		if( !parameters.is_fullscreen )
		{
			present_parameters.BackBufferWidth = 0;
			present_parameters.BackBufferHeight = 0;
		}
		hr = device->Reset( &present_parameters );
		if( FAILED( hr ) )
		{
			logs::log() << logs::warning << "Hardware::start_frame(): device->Reset() failed hr=" << hr << "\n";
			return false;
		}
		current_rect = new_rect;
		restore_resources();
		return false;
	}
	if( !device_objects )
		throw Gluck(__FUNCTION__, "DirectGraphics 9 reported the device is no more operational");

 	device_objects->issue_frame_query();
	device_objects->wait_frame_query();

	report_device_memory();
	return true;
}

void Hardware::flip()
{
	if( device->Present( 0, 0, 0, 0 ) != D3D_OK )
		logs::log() << logs::warning << "Present returned BAD status\n";
}

DWORD Hardware::get_dword_color(const Color & color, double alpha)
{
	return math::clamp( int(255*color.b),0,255) + 
		(math::clamp( int(255*color.g),0,255) << 8) + 
		(math::clamp( int(255*color.r),0,255) << 16) + 
		(math::clamp( int(255*alpha),0,255) << 24);
}

DWORD Hardware::get_dword_color(double r, double g, double b, double alpha)
{
	return math::clamp( int(255*b),0,255) + 
		(math::clamp( int(255*g),0,255) << 8) + 
		(math::clamp( int(255*r),0,255) << 16) + 
		(math::clamp( int(255*alpha),0,255) << 24);
}

Hardware::Tables::Tables(IDirect3DDevice9 * device, style_enum style, D3DFORMAT format_for_texture)
:	dummy_05_05_1_1( 0 ),
	dummy_0_0_0_0( 0 ),
	dummy_1_1_1_1( 0 )
{
	D3DLOCKED_RECT locked_rect;
	DWORD * pBits;

	hr = device->CreateTexture( 1, 1, 1, 0, format_for_texture, D3DPOOL_MANAGED, &dummy_05_05_1_1, 0 );
	if( dummy_05_05_1_1 && dummy_05_05_1_1->LockRect( 0, &locked_rect, 0, D3DLOCK_NOSYSLOCK ) == D3D_OK )
	{
		pBits = static_cast<DWORD*>( locked_rect.pBits );
		*pBits = Hardware::get_dword_color( 0.5, 0.5, 1, 1 );
		dummy_05_05_1_1->UnlockRect( 0 );
	}

	hr = device->CreateTexture( 1, 1, 1, 0, D3DFMT_A8R8G8B8, D3DPOOL_MANAGED, &dummy_0_0_0_0, 0 );
	if( dummy_0_0_0_0 && dummy_0_0_0_0->LockRect( 0, &locked_rect, 0, D3DLOCK_NOSYSLOCK ) == D3D_OK )
	{
		pBits = static_cast<DWORD*>( locked_rect.pBits );
		*pBits = Hardware::get_dword_color( 0, 0, 0, 0 );
		dummy_0_0_0_0->UnlockRect( 0 );
	}

	hr = device->CreateTexture( 1, 1, 1, 0, D3DFMT_A8R8G8B8, D3DPOOL_MANAGED, &dummy_1_1_1_1, 0 );
	if( dummy_1_1_1_1 && dummy_1_1_1_1->LockRect( 0, &locked_rect, 0, D3DLOCK_NOSYSLOCK ) == D3D_OK )
	{
		pBits = static_cast<DWORD*>( locked_rect.pBits );
		*pBits = Hardware::get_dword_color( 1, 1, 1, 1 );
		dummy_1_1_1_1->UnlockRect( 0 );
	}
}

Hardware::Tables::~Tables()
{
	if( dummy_05_05_1_1 )
		dummy_05_05_1_1->Release();
	dummy_05_05_1_1 = 0;
	if( dummy_0_0_0_0 )
		dummy_0_0_0_0->Release();
	dummy_0_0_0_0 = 0;
	if( dummy_1_1_1_1 )
		dummy_1_1_1_1->Release();
	dummy_1_1_1_1 = 0;
}

#include "shaders/common/_defines.vsh"

#define dwVertexShader VSCommonSpriteSrc
#include "shaders/common/out/v_sprite.h"
#undef dwVertexShader

void Hardware::set_culling(DWORD cull_order)
{
	device->SetRenderState( D3DRS_CULLMODE, cull_order );
}

static D3DMATERIAL9 current_material;

static void prepare_model( const RenderModel & render_model, IDirect3DDevice9 * device, bool is_opaque )
{
	if(render_model.filter_texture)
	{
		device->SetSamplerState( 0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR );
		device->SetSamplerState( 0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR );
		device->SetSamplerState( 0, D3DSAMP_MIPFILTER, D3DTEXF_LINEAR );
		device->SetSamplerState( 1, D3DSAMP_MINFILTER, D3DTEXF_LINEAR );
		device->SetSamplerState( 1, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR );
		device->SetSamplerState( 1, D3DSAMP_MIPFILTER, D3DTEXF_LINEAR );
	}
	else
	{
		device->SetSamplerState( 0, D3DSAMP_MINFILTER, D3DTEXF_POINT );
		device->SetSamplerState( 0, D3DSAMP_MAGFILTER, D3DTEXF_POINT );
		device->SetSamplerState( 0, D3DSAMP_MIPFILTER, D3DTEXF_NONE );
		device->SetSamplerState( 1, D3DSAMP_MINFILTER, D3DTEXF_POINT );
		device->SetSamplerState( 1, D3DSAMP_MAGFILTER, D3DTEXF_POINT );
		device->SetSamplerState( 1, D3DSAMP_MIPFILTER, D3DTEXF_NONE );
	}

	Matrix mat = render_model.world;
	mat.transpose();
	device->SetTransform( D3DTS_WORLD, (D3DMATRIX *)&mat );

	const double opacity = 1 - render_model.material.transparency;
	// Very funny! We multiply alpha by 2 in the shader for color-key purposes, 
	// and we should compensate here for Transparent models only!
	const double compensated_opacity = is_opaque ? opacity : (opacity / 2);

	// Set the RGBA for diffuse reflection.
	D3DMATERIAL9 material;
	material.Diffuse.r = render_model.material.diffuse.r * opacity / 2;
	material.Diffuse.g = render_model.material.diffuse.g * opacity / 2;
	material.Diffuse.b = render_model.material.diffuse.b * opacity / 2;
	material.Diffuse.a = compensated_opacity;

	// Set the RGBA for ambient reflection.
	material.Ambient.r = render_model.material.diffuse.r * opacity / 2;
	material.Ambient.g = render_model.material.diffuse.g * opacity / 2;
	material.Ambient.b = render_model.material.diffuse.b * opacity / 2;
	material.Ambient.a = 0;

	// Set the color and sharpness of specular highlights.
	material.Specular.r = 0;//render_model.material.specular.r;
	material.Specular.g = 0;//render_model.material.specular.g;
	material.Specular.b = 0;//render_model.material.specular.b;
	material.Specular.a = 0;//1;
	material.Power = 0;//render_model.material.specular_power;

	// Set the RGBA for emissive color.
	material.Emissive.r = 0;//render_model.material.emissive.r;
	material.Emissive.g = 0;//render_model.material.emissive.g;
	material.Emissive.b = 0;//render_model.material.emissive.b;
	material.Emissive.a = 0;//opacity;

	current_material = material;

	device->SetMaterial( &material );
}

static void prepare_model( const RenderSprite & render_model, IDirect3DDevice9 * device, bool is_opaque )
{
	device->SetSamplerState( 0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR );
	device->SetSamplerState( 0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR );
	device->SetSamplerState( 0, D3DSAMP_MIPFILTER, D3DTEXF_LINEAR );
	device->SetSamplerState( 1, D3DSAMP_MINFILTER, D3DTEXF_LINEAR );
	device->SetSamplerState( 1, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR );
	device->SetSamplerState( 1, D3DSAMP_MIPFILTER, D3DTEXF_LINEAR );

	Matrix mat = render_model.world;
	mat.transpose();
	device->SetTransform( D3DTS_WORLD, (D3DMATRIX *)&mat );

	const double opacity = 1 - render_model.material.transparency;
	// Very funny! We multiply alpha by 2 in the shader for color-key purposes, 
	// and we should compensate here for Transparent models only!
	const double compensated_opacity = is_opaque ? opacity : (opacity / 2);

	// Set the RGBA for diffuse reflection.
	D3DMATERIAL9 material;
	material.Diffuse.r = render_model.material.diffuse.r * opacity / 2;
	material.Diffuse.g = render_model.material.diffuse.g * opacity / 2;
	material.Diffuse.b = render_model.material.diffuse.b * opacity / 2;
	material.Diffuse.a = compensated_opacity;

	// Set the RGBA for ambient reflection.
	material.Ambient.r = render_model.material.diffuse.r * opacity / 2;
	material.Ambient.g = render_model.material.diffuse.g * opacity / 2;
	material.Ambient.b = render_model.material.diffuse.b * opacity / 2;
	material.Ambient.a = 0;

	// Set the color and sharpness of specular highlights.
	material.Specular.r = 0;//render_model.material.specular.r;
	material.Specular.g = 0;//render_model.material.specular.g;
	material.Specular.b = 0;//render_model.material.specular.b;
	material.Specular.a = 0;//1;
	material.Power = 0;//render_model.material.specular_power;

	// Set the RGBA for emissive color.
	material.Emissive.r = 0;//render_model.material.emissive.r;
	material.Emissive.g = 0;//render_model.material.emissive.g;
	material.Emissive.b = 0;//render_model.material.emissive.b;
	material.Emissive.a = 0;//opacity;

	current_material = material;

	device->SetMaterial( &material );
}

void Hardware::set_lights( RenderList * render_list )
{
	int i = 0;

	if(render_list->lights.size() == 0)
	{
		while( i++ < 8 )
			device->LightEnable(i, FALSE);

		device->SetRenderState(D3DRS_LIGHTING, FALSE);
		return;
	}
	device->SetRenderState(D3DRS_LIGHTING, TRUE);

	RenderLights::const_iterator lit = render_list->lights.begin();
	for( ; lit != render_list->lights.end(); ++lit )
	{
		D3DLIGHT9 d3dLight;

		// Initialize the structure.
		memset( &d3dLight, 0, sizeof(D3DLIGHT9) );

		d3dLight.Diffuse.r  = lit->diffuse.r;
		d3dLight.Diffuse.g  = lit->diffuse.g;
		d3dLight.Diffuse.b  = lit->diffuse.b;
		d3dLight.Ambient.r  = 0.0f;
		d3dLight.Ambient.g  = 0.0f;
		d3dLight.Ambient.b  = 0.0f;
		d3dLight.Specular.r = 0.0f;
		d3dLight.Specular.g = 0.0f;
		d3dLight.Specular.b = 0.0f;

		if( !lit->is_directional )
		{
			d3dLight.Type = D3DLIGHT_POINT;
			//in world space
			d3dLight.Position.x = lit->world_position.x;
			d3dLight.Position.y = lit->world_position.y;
			d3dLight.Position.z = lit->world_position.z;

			//attenuate light
			d3dLight.Attenuation0 = lit->clq.x/lit->intensity;
			d3dLight.Attenuation1 = lit->clq.y/lit->intensity;
			d3dLight.Attenuation2 = lit->clq.z/lit->intensity;
			d3dLight.Range        = 6*lit->range;
		}
		else
		{
			d3dLight.Type = D3DLIGHT_DIRECTIONAL;

			//attenuate light
			d3dLight.Diffuse.r  *= lit->intensity;
			d3dLight.Diffuse.g  *= lit->intensity;
			d3dLight.Diffuse.b  *= lit->intensity;

			//in world space
			d3dLight.Direction.x = lit->world_direction.x;
			d3dLight.Direction.y = lit->world_direction.y;
			d3dLight.Direction.z = -lit->world_direction.z;
		}

		device->SetLight( i, &d3dLight );
		device->LightEnable(i, TRUE);
		++i;
		if( i >= 8 )
			break;
	}

	while( i < 8 )
	{
		device->LightEnable(i, FALSE);
		++i;
	}
}

void Hardware::set_alpha_test_off()
{
	hr = device->SetRenderState( D3DRS_ALPHATESTENABLE, FALSE );
}

void Hardware::set_alpha_test_on()
{
	hr = device->SetRenderState( D3DRS_ALPHATESTENABLE, TRUE );
}

void Hardware::set_alpha_test_discard_only_0()
{
	hr = device->SetRenderState( D3DRS_ALPHAFUNC, D3DCMP_NOTEQUAL );
	hr = device->SetRenderState( D3DRS_ALPHAREF, 0 );
	hr = device->SetRenderState( D3DRS_ALPHATESTENABLE, TRUE );
}

void Hardware::set_alpha_test_keep_only_1()
{
	hr = device->SetRenderState( D3DRS_ALPHAFUNC, D3DCMP_EQUAL );
	hr = device->SetRenderState( D3DRS_ALPHAREF, 0xFF );
	hr = device->SetRenderState( D3DRS_ALPHATESTENABLE, TRUE );
}

void Hardware::set_z_less_write()
{
	device->SetRenderState( D3DRS_ZFUNC, D3DCMP_LESS );
	device->SetRenderState( D3DRS_ZWRITEENABLE, true );
}

void Hardware::set_z_less_nowrite()
{
	device->SetRenderState( D3DRS_ZFUNC, D3DCMP_LESS );
	device->SetRenderState( D3DRS_ZWRITEENABLE, false );
}

void Hardware::set_z_lessequal_nowrite()
{
	device->SetRenderState( D3DRS_ZFUNC, D3DCMP_LESSEQUAL );
	device->SetRenderState( D3DRS_ZWRITEENABLE, false );
}

void Hardware::set_z_lessequal_write()
{
	device->SetRenderState( D3DRS_ZFUNC, D3DCMP_LESSEQUAL );
	device->SetRenderState( D3DRS_ZWRITEENABLE, true );
}

void Hardware::set_blend_mode(BlendMode bm)
{
	if (current_blend_mode == bm)
		return;

	switch (bm)
	{
	case BM_ALPHA:
		device->SetRenderState( D3DRS_SRCBLEND, D3DBLEND_SRCALPHA );
		device->SetRenderState( D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA );
		break;
	case BM_ADD:
		device->SetRenderState( D3DRS_SRCBLEND, D3DBLEND_ONE );
		device->SetRenderState( D3DRS_DESTBLEND, D3DBLEND_ONE );
		break;
	case BM_ADD_ALPHA:
		device->SetRenderState( D3DRS_SRCBLEND, D3DBLEND_SRCALPHA );
		device->SetRenderState( D3DRS_DESTBLEND, D3DBLEND_ONE );
		break;
	default:
		LIBA_FAIL("Unexpected blend mode");
	}
}

void Hardware::combiner_blend_for_sprite_pass()
{
	device->SetTextureStageState( 0, D3DTSS_COLORARG1, D3DTA_TEXTURE );
	device->SetTextureStageState( 0, D3DTSS_COLORARG2, D3DTA_CURRENT );
	device->SetTextureStageState( 0, D3DTSS_COLOROP,   D3DTOP_MODULATE );
	device->SetTextureStageState( 0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE );
	device->SetTextureStageState( 0, D3DTSS_ALPHAARG2, D3DTA_CURRENT );
	device->SetTextureStageState( 0, D3DTSS_ALPHAOP,   D3DTOP_MODULATE );

	device->SetTextureStageState( 1, D3DTSS_COLORARG1, D3DTA_TFACTOR );
	device->SetTextureStageState( 1, D3DTSS_COLORARG2, D3DTA_CURRENT );
	device->SetTextureStageState( 1, D3DTSS_COLOROP,   D3DTOP_MODULATE );
	device->SetTextureStageState( 1, D3DTSS_ALPHAARG1, D3DTA_TFACTOR );
	device->SetTextureStageState( 1, D3DTSS_ALPHAARG2, D3DTA_CURRENT );
	device->SetTextureStageState( 1, D3DTSS_ALPHAOP,   D3DTOP_MODULATE );

	//device->SetRenderState( D3DRS_SRCBLEND, D3DBLEND_ONE );
	set_blend_mode(BM_ALPHA);
}

void Hardware::combiner_blend_for_diffuse_reflecion_emissive_pass()
{
	// for new cards, we calc rgb = Dcol.rgb*Dtex.rgb*Dcol.a*Dtex.a + Ecol.rgb*Etex.rgb + ...
	// for old cards, we calc rgb = Dcol.a*Dtex.a*( Dcol.rgb*Dtex.rgb + Ecol.rgb*Etex.rgb + ... )
	// for all cards, we calc   a = Dcol.a*Dtex.a*Etex.a
	switch( style )
	{
	case PS14:
	case PS13:
	case PS00:
		device->SetTextureStageState( 0, D3DTSS_COLORARG1, D3DTA_TEXTURE );
		device->SetTextureStageState( 0, D3DTSS_COLORARG2, D3DTA_CURRENT );
		device->SetTextureStageState( 0, D3DTSS_COLOROP,   D3DTOP_MODULATE );
		device->SetTextureStageState( 0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE );
		device->SetTextureStageState( 0, D3DTSS_ALPHAARG2, D3DTA_CURRENT );
		device->SetTextureStageState( 0, D3DTSS_ALPHAOP,   D3DTOP_MODULATE );

		device->SetTextureStageState( 1, D3DTSS_COLORARG1, D3DTA_TEXTURE );
		device->SetTextureStageState( 1, D3DTSS_COLORARG2, D3DTA_CURRENT );
		device->SetTextureStageState( 1, D3DTSS_COLOROP,   D3DTOP_MODULATE );
		device->SetTextureStageState( 1, D3DTSS_ALPHAARG1, D3DTA_TEXTURE );
		device->SetTextureStageState( 1, D3DTSS_ALPHAARG2, D3DTA_CURRENT );
		device->SetTextureStageState( 1, D3DTSS_ALPHAOP,   D3DTOP_MODULATE );

		device->SetTextureStageState( 2, D3DTSS_COLORARG1, D3DTA_TFACTOR );
		device->SetTextureStageState( 2, D3DTSS_COLORARG2, D3DTA_CURRENT );
		device->SetTextureStageState( 2, D3DTSS_COLOROP,   D3DTOP_MODULATE );
		device->SetTextureStageState( 2, D3DTSS_ALPHAARG1, D3DTA_TFACTOR );
		device->SetTextureStageState( 2, D3DTSS_ALPHAARG2, D3DTA_CURRENT );
		device->SetTextureStageState( 2, D3DTSS_ALPHAOP,   D3DTOP_MODULATE );

		set_blend_mode(BM_ALPHA);

		break;
	case SLOW:
		device->SetTextureStageState( 0, D3DTSS_COLORARG1, D3DTA_TEXTURE );
		device->SetTextureStageState( 0, D3DTSS_COLORARG2, D3DTA_CURRENT );
		device->SetTextureStageState( 0, D3DTSS_COLOROP,   D3DTOP_MODULATE );
		device->SetTextureStageState( 0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE );
		device->SetTextureStageState( 0, D3DTSS_ALPHAARG2, D3DTA_CURRENT );
		device->SetTextureStageState( 0, D3DTSS_ALPHAOP,   D3DTOP_MODULATE );

		device->SetTextureStageState( 1, D3DTSS_COLORARG1, D3DTA_TFACTOR );
		device->SetTextureStageState( 1, D3DTSS_COLORARG2, D3DTA_CURRENT );
		device->SetTextureStageState( 1, D3DTSS_COLOROP,   D3DTOP_MODULATE );
		device->SetTextureStageState( 1, D3DTSS_ALPHAARG1, D3DTA_TFACTOR );
		device->SetTextureStageState( 1, D3DTSS_ALPHAARG2, D3DTA_CURRENT );
		device->SetTextureStageState( 1, D3DTSS_ALPHAOP,   D3DTOP_MODULATE );

		set_blend_mode(BM_ALPHA);
		break;
	}
}

void Hardware::ren_model( const RenderList & render_list, const RenderSprite & render_model )
{
	render_model.vb->render( render_model.primitive_start, render_model.primitive_count );
}

void Hardware::ren_model( const RenderList & render_list, const RenderModel & render_model )
{
	if( render_model.ib )
		render_model.vb->render( render_model.ib, render_model.primitive_start, render_model.primitive_count, render_model.vertex_start, render_model.vertex_count );
	else
		render_model.vb->render( render_model.primitive_start, render_model.primitive_count );
}

void Hardware::con_tex_ren_model_diffuse_reflecion_emissive( const RenderList & render_list, const RenderModel & render_model )
{
	TextureBase * ref = render_model.material.cubic_reflection_texture.texture;
	TextureBase * dif = render_model.material.diffuse_texture.texture;
	TextureBase * emi = render_model.material.emissive_texture.texture;
	switch( style )
	{
	case PS14:
	case PS13:
	case PS00:
		device->SetTextureStageState( 1, D3DTSS_TEXCOORDINDEX, 0 );
		device->SetRenderState( D3DRS_TEXTUREFACTOR, get_dword_color(render_model.material.emissive, 1.0f - render_model.material.transparency) );
		set_texture_alt( 0,	dif, tables->dummy_1_1_1_1 );
		set_texture_alt( 1,	emi, tables->dummy_1_1_1_1  );
		set_texture_alt( 2,	NULL, tables->dummy_1_1_1_1  );

		set_blend_mode(render_model.blendMode);
		ren_model( render_list, render_model );
		break;
	case SLOW:
		if( dif )
		{
			set_texture_alt( 0,	dif, tables->dummy_1_1_1_1 );

			ren_model( render_list, render_model );
		}
		else
		{
			set_texture_alt( 0,	emi, tables->dummy_1_1_1_1 );

			D3DMATERIAL9 material = current_material;
			material.Emissive.r = render_model.material.emissive.r/2;
			material.Emissive.g = render_model.material.emissive.g/2;
			material.Emissive.b = render_model.material.emissive.b/2;
			material.Diffuse.r = 0;
			material.Diffuse.g = 0;
			material.Diffuse.b = 0;
			device->SetMaterial( &material );

			//device->SetRenderState( D3DRS_SRCBLEND, D3DBLEND_ONE );
			set_blend_mode(render_model.blendMode);
			ren_model( render_list, render_model );
		}
		break;
	}
}

void Hardware::con_tex_ren_model_diffuse_reflecion_emissive( const RenderList & render_list, const RenderSprite & render_model )
{
	TextureBase * ref = render_model.material.cubic_reflection_texture.texture;
	TextureBase * dif = render_model.material.diffuse_texture.texture;
	TextureBase * emi = render_model.material.emissive_texture.texture;

#if TRUE
	if( dif )
	{
		set_texture_alt( 0,	dif, tables->dummy_1_1_1_1 );

		ren_model( render_list, render_model );
	}
	else
	{
		set_texture_alt( 0,	emi, tables->dummy_1_1_1_1 );

		D3DMATERIAL9 material = current_material;
		material.Emissive.r = render_model.material.emissive.r;
		material.Emissive.g = render_model.material.emissive.g;
		material.Emissive.b = render_model.material.emissive.b;
		material.Diffuse.r = 0;
		material.Diffuse.g = 0;
		material.Diffuse.b = 0;
		device->SetMaterial( &material );

		//device->SetRenderState( D3DRS_SRCBLEND, D3DBLEND_ONE );
		device->SetRenderState( D3DRS_SRCBLEND, D3DBLEND_SRCALPHA );
		device->SetRenderState( D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA );
		ren_model( render_list, render_model );
		device->SetRenderState( D3DRS_SRCBLEND, D3DBLEND_SRCALPHA );
	}
#else
		//old version, doesn't work properly
	switch( style )
	{
	case PS14:
	case PS13:
	case PS00:
		{
		device->SetTextureStageState( 1, D3DTSS_TEXCOORDINDEX, 0 );
		device->SetRenderState( D3DRS_TEXTUREFACTOR, get_dword_color(render_model.material.emissive, 1) );
		if(dif)
		{
			set_texture_alt( 0,	dif, tables->dummy_1_1_1_1 );
		}
		else
		{
			set_texture_alt( 0,	emi, tables->dummy_1_1_1_1  );
		}

		if( dif )
			ren_model( render_list, render_model );
		else
		{
			device->SetRenderState( D3DRS_SRCBLEND, D3DBLEND_SRCALPHA );
			ren_model( render_list, render_model );
			device->SetRenderState( D3DRS_SRCBLEND, D3DBLEND_SRCALPHA );
		}
		}
		break;
	case SLOW:
		if( dif )
		{
			set_texture_alt( 0,	dif, tables->dummy_1_1_1_1 );

			ren_model( render_list, render_model );
		}
		else
		{
			set_texture_alt( 0,	emi, tables->dummy_1_1_1_1 );

			D3DMATERIAL8 material = current_material;
			material.Emissive.r = render_model.material.emissive.r/2;
			material.Emissive.g = render_model.material.emissive.g/2;
			material.Emissive.b = render_model.material.emissive.b/2;
			material.Diffuse.r = 0;
			material.Diffuse.g = 0;
			material.Diffuse.b = 0;
			device->SetMaterial( &material );

			device->SetRenderState( D3DRS_SRCBLEND, D3DBLEND_ONE );
			//			device->SetTextureStageState( 0, D3DTSS_COLOROP, D3DTOP_MODULATE );
			ren_model( render_list, render_model );
			//			device->SetTextureStageState( 0, D3DTSS_COLOROP, D3DTOP_MODULATE2X );
			device->SetRenderState( D3DRS_SRCBLEND, D3DBLEND_SRCALPHA );
		}
		break;
	}
#endif
}

/*
static void create_vsha(IDirect3DDevice9 * device, const DWORD * decl, const DWORD * vsha_src, DWORD * vsha, bool use_hw_shaders )
{
	if( !*vsha )
		hr = device->CreateVertexShader( decl, vsha_src, vsha, use_hw_shaders ? 0 : D3DUSAGE_SOFTWAREPROCESSING );
}
*/

bool Hardware::start_render( const RenderList & render_list, RenderTarget * render_target )
{
	Viewport vp = render_list.viewport;
	if( !render_target->start_scene(vp.left, vp.right, vp.top, vp.bottom) )
		return false;

	Color use_color(0,0,0);
	if( render_list.fog_range != 0)
		use_color = render_list.fog_color;

/*	DWORD clear_flags = 0;
	if( parameters.z_buffer_format == D3DFMT_D24S8 ||
		parameters.z_buffer_format == D3DFMT_D24X4S4 ||
		parameters.z_buffer_format == D3DFMT_D15S1 )
	{
		clear_flags = D3DCLEAR_STENCIL;
	}
	if( render_list.clear_render_target )
		device->Clear( 0, 0, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER | clear_flags, get_dword_color(use_color, 1), 1.0, 0x80 );
	else
		device->Clear( 0, 0, D3DCLEAR_ZBUFFER | clear_flags, 0, 1.0, 0x80 );
		*/

	if( render_list.clear_render_target )
		device->Clear( 0, 0, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, get_dword_color(use_color, 1), 1.0, 0 );
	else
		device->Clear( 0, 0, D3DCLEAR_ZBUFFER, 0, 1.0, 0 );
/*
	device->SetTextureStageState( 0, D3DTSS_ADDRESSU, D3DTADDRESS_WRAP );
	device->SetTextureStageState( 0, D3DTSS_ADDRESSV, D3DTADDRESS_WRAP );
	device->SetTextureStageState( 0, D3DTSS_ADDRESSW, D3DTADDRESS_WRAP );
	device->SetTextureStageState( 1, D3DTSS_ADDRESSU, D3DTADDRESS_WRAP );
	device->SetTextureStageState( 1, D3DTSS_ADDRESSV, D3DTADDRESS_WRAP );
	device->SetTextureStageState( 1, D3DTSS_ADDRESSW, D3DTADDRESS_WRAP );
	device->SetTextureStageState( 2, D3DTSS_ADDRESSU, D3DTADDRESS_CLAMP );
	device->SetTextureStageState( 2, D3DTSS_ADDRESSV, D3DTADDRESS_CLAMP );
	device->SetTextureStageState( 2, D3DTSS_ADDRESSW, D3DTADDRESS_CLAMP );
	device->SetTextureStageState( 3, D3DTSS_ADDRESSU, D3DTADDRESS_CLAMP );
	device->SetTextureStageState( 3, D3DTSS_ADDRESSV, D3DTADDRESS_CLAMP );
	device->SetTextureStageState( 3, D3DTSS_ADDRESSW, D3DTADDRESS_CLAMP );
	device->SetTextureStageState( 4, D3DTSS_ADDRESSU, D3DTADDRESS_CLAMP );
	device->SetTextureStageState( 4, D3DTSS_ADDRESSV, D3DTADDRESS_CLAMP );
	device->SetTextureStageState( 4, D3DTSS_ADDRESSW, D3DTADDRESS_CLAMP );
*/
/*
	device->SetTextureStageState( 0, D3DTSS_MINFILTER, D3DTEXF_LINEAR );
	device->SetTextureStageState( 0, D3DTSS_MAGFILTER, D3DTEXF_LINEAR );
	device->SetTextureStageState( 0, D3DTSS_MIPFILTER, D3DTEXF_LINEAR );
	device->SetTextureStageState( 1, D3DTSS_MINFILTER, D3DTEXF_LINEAR );
	device->SetTextureStageState( 1, D3DTSS_MAGFILTER, D3DTEXF_LINEAR );
	device->SetTextureStageState( 1, D3DTSS_MIPFILTER, D3DTEXF_LINEAR );
*/
//	device->SetTexture( 0, 0 );
//	device->SetTexture( 1, 0 );
/*
	device->SetRenderState( D3DRS_ALPHABLENDENABLE, TRUE );
	device->SetRenderState( D3DRS_SPECULARENABLE, FALSE );//TRUE );
	*/
	return true;
}

void Hardware::render( RenderList * rl, RenderTarget * render_target )
{
#ifdef SPEED_TEST
	__int64 now = threads::get_precise_tick_count();
#endif

	current_render_list = rl;
	const RenderList & render_list = *rl;

	{
#ifdef SPEED_TEST
	__int64 now = threads::get_precise_tick_count();
#endif
		rl->calculate( parameters.shadow_passes, parameters.lighting_passes );
		set_lights( rl );

#ifdef SPEED_TEST
	int passed = threads::get_precise_tick_count() - now;
	logs::log() << "\t\t\trl->calculate:" << passed * threads::get_precise_tick_time()*1000 << " msec\n";
#endif
	}
#ifdef SPEED_TEST
	logs::log() << "Rendering: " << rl->models.size() << " models, " << rl->shadow_models.size() << " shadow_models, " << rl->sprites.size() << " sprites (" << rl->models.size() - rl->first_transparent_model << " transparent models)\n";
#endif

	{
#ifdef SPEED_TEST
	__int64 now = threads::get_precise_tick_count();
#endif
		if( !start_render( render_list, render_target ) )
			return;

		Matrix mat = rl->camera_projection;
		mat.transpose();
		device->SetTransform( D3DTS_PROJECTION, (D3DMATRIX *)&mat );

		mat = rl->view_mat;
		mat.transpose();
		device->SetTransform( D3DTS_VIEW, (D3DMATRIX *)&mat );
		//device->SetRenderState( D3DRS_AMBIENT, get_dword_color( rl->ambient_color.r, rl->ambient_color.g, rl->ambient_color.b, 1 ) );
		device->SetRenderState( D3DRS_AMBIENT, get_dword_color( 1, 1, 1, 1 ) );

#ifdef SPEED_TEST
	int passed = threads::get_precise_tick_count() - now;
	logs::log() << "\t\t\tstart_render:" << passed * threads::get_precise_tick_time()*1000 << " msec\n";
#endif
	}

	// -------------------------- Solid Models, Diffuse+Reflection+Emissive
	{
#ifdef SPEED_TEST
	__int64 now = threads::get_precise_tick_count();
#endif
	//TODO: MAX WAS HERE
	set_culling(D3DCULL_CCW);
#ifdef FOR_TUX
	set_z_lessequal_write();
	set_alpha_test_off();
#else
	set_z_less_write();
	set_alpha_test_keep_only_1();
#endif
//	set_fog( render_list.fog_color, (render_list.fog_range != 0) );

		device->SetFVF( D3DFVF_XYZ | D3DFVF_NORMAL | D3DFVF_TEX1 );
		combiner_blend_for_diffuse_reflecion_emissive_pass();
		{
			RenderOrder::const_iterator cit = render_list.render_order.begin();
			for( ; cit != render_list.render_order.begin() + render_list.first_transparent_model; ++cit )
			{
				const RenderModel * model = &render_list.models[cit->number]; 
				prepare_model( *model, device, true );
				con_tex_ren_model_diffuse_reflecion_emissive( render_list, *model );
			}
		}

#ifdef SPEED_TEST
	int passed = threads::get_precise_tick_count() - now;
	logs::log() << "\t\t\tDiffuseReflectionEmissive:" << passed * threads::get_precise_tick_time()*1000 << " msec\n";
#endif
	}

	// -------------------------- Transparent Models
	{
#ifdef SPEED_TEST
	__int64 now = threads::get_precise_tick_count();
#endif
		set_alpha_test_off();
		set_z_lessequal_nowrite();

		bool sprites_combiner_set = false;
		RenderOrder::const_iterator cit = render_list.render_order.begin() + render_list.first_transparent_model;
		for( ; cit != render_list.render_order.end(); ++cit ) 
		{
			if(cit->is_sprite)
			{
				if(!sprites_combiner_set)
				{
					sprites_combiner_set = true;
					combiner_blend_for_sprite_pass();
					set_culling(D3DCULL_NONE);
//					hr = device->SetVertexShader( device_objects->VSSprite );
				}

				const RenderSprite * sprite = &render_list.sprites[cit->number]; 
				prepare_model( *sprite, device, false );
				con_tex_ren_model_diffuse_reflecion_emissive( render_list, *sprite );

//				con_sprite( render_list, *sprite );
//				tex_ren_sprite( render_list, *sprite );
			}
			else
			{
				if(sprites_combiner_set)
				{
					sprites_combiner_set = false;
					combiner_blend_for_diffuse_reflecion_emissive_pass();
					set_culling(D3DCULL_CCW);
				}

				const RenderModel * model = &render_list.models[cit->number];
				prepare_model( *model, device, false );
				con_tex_ren_model_diffuse_reflecion_emissive( render_list, *model );
			}
		}

#ifdef SPEED_TEST
	int passed = threads::get_precise_tick_count() - now;
	logs::log() << "\t\t\tNoShadow Lights on Transparent models:" << passed * threads::get_precise_tick_time()*1000 << " msec\n";
#endif
	}
	// -------------------------- Sprites
/*	{
#ifdef SPEED_TEST
	__int64 now = threads::get_precise_tick_count();
#endif
		if( try_hardware_vp && !use_hardware_vshaders )
			device->SetRenderState( D3DRS_SOFTWAREVERTEXPROCESSING, true );

		set_culling(D3DCULL_NONE);
		combiner_blend_for_sprite_pass();
		hr = device->SetVertexShader( device_objects->VSSprite );

		RenderSprites::const_iterator sit = render_list.sprites.begin();
		for( ; sit != render_list.sprites.end(); ++sit )
		{
			con_sprite( render_list, *sit );
			tex_ren_sprite( render_list, *sit );
		}

		if( try_hardware_vp && !use_hardware_vshaders )
			device->SetRenderState( D3DRS_SOFTWAREVERTEXPROCESSING, false );

#ifdef SPEED_TEST
	int passed = threads::get_precise_tick_count() - now;
	logs::log() << "\t\t\tSprites:" << passed * threads::get_precise_tick_time()*1000 << " msec\n";
#endif
	}
*/	{
#ifdef SPEED_TEST
	__int64 now = threads::get_precise_tick_count();
#endif
		finish_scene();
#ifdef SPEED_TEST
	int passed = threads::get_precise_tick_count() - now;
	logs::log() << "\t\t\tfinish_scene:" << passed * threads::get_precise_tick_time()*1000 << " msec\n";
#endif
	}

#ifdef SPEED_TEST
	int passed = threads::get_precise_tick_count() - now;
	logs::log() << "\t\t\tPassed: " << passed * threads::get_precise_tick_time()*1000 << " msec\n";
#endif
}

Hardware::DeviceObjects::DeviceObjects(IDirect3DDevice9 * device, style_enum style, bool use_hw_shaders, D3DFORMAT render_target_format):
	device( device ),
	use_hw_shaders( use_hw_shaders ),
	render_target_default( 0 ),
	z_buffer_surface( 0 ),
	lock_surf1( 0 ),
	lock_surf2( 0 ),
	first_lock( false )
{
	VSSprite = 0;

	if( render_target_format != D3DFMT_UNKNOWN )
	{
		device->CreateRenderTarget( 2, 2, render_target_format, D3DMULTISAMPLE_NONE, 0, true, &lock_surf1, 0 );
		device->CreateRenderTarget( 2, 2, render_target_format, D3DMULTISAMPLE_NONE, 0, true, &lock_surf2, 0 );
	}

	HRESULT hr = device->GetRenderTarget( 0, &render_target_default );
	hr = device->GetDepthStencilSurface( &z_buffer_surface );

	D3DGAMMARAMP ramp;
	for( int i = 0; i < 256; ++i )
	{
		double val = i / 255.0;
		ramp.red[i] = sqrt( val ) * 65535;
		ramp.green[i] = sqrt( val ) * 65535;
		ramp.blue[i] = sqrt( val ) * 65535;
	}
///	device->SetGammaRamp( 0, D3DSGR_CALIBRATE, &ramp );

//	create_vsha(device, VertexShaderPointDecl, VSCommonSpriteSrc, &VSSprite, use_hw_shaders);
}

void Hardware::DeviceObjects::recreate_z_buffer_surface(int wi, int he, D3DFORMAT z_buffer_format)
{
/*	HRESULT hr = device->SetDepthStencilSurface( 0 );
	hr = GlobalRelease( &z_buffer_surface );
	hr = device->CreateDepthStencilSurface( wi, he, z_buffer_format,
		D3DMULTISAMPLE_NONE, 0, true, &z_buffer_surface, 0 );
	if( !z_buffer_surface )
		throw Gluck(__FUNCTION__,"Cannot create z_buffer_surface");
	hr = device->SetDepthStencilSurface( z_buffer_surface );*/
}

void Hardware::DeviceObjects::issue_frame_query()
{
	HRESULT hr = 0;
/*	if( !frame_query )
		hr = device->CreateQuery( D3DQUERYTYPE_EVENT, &frame_query );
	if( frame_query )
		hr = frame_query->Issue( D3DISSUE_END );
		*/
}

void Hardware::DeviceObjects::wait_frame_query()
{
/*	if( frame_query )
	{
		BOOL data = FALSE;
		while( (hr = frame_query->GetData( &data, sizeof(BOOL), D3DGETDATA_FLUSH )) == S_FALSE )
		{
			logs::log() << ".";
			threads::sleep( 0 );	
		}
		frame_query->Release(); frame_query = 0;
	}
	*/
	if( lock_surf1 && lock_surf2 )
	{
		if( first_lock )
		{
			first_lock = !first_lock;
			//render to texture0
			device->SetRenderTarget( 0, lock_surf1 );
			device->SetDepthStencilSurface(0);
			static float verts[]= { 0, 0, 0,
									1, 1, 1,
									1, 0, 1	};
			device->BeginScene();
			device->SetFVF( D3DFVF_XYZ );
			device->DrawPrimitiveUP( D3DPT_TRIANGLELIST, 1, verts, 12 );
			device->EndScene();
			//read texture1
			D3DLOCKED_RECT locked_rect;
			if( lock_surf2->LockRect( &locked_rect, 0, D3DLOCK_NOSYSLOCK ) != D3D_OK )
				return;
			int temp_var = *((char *)locked_rect.pBits);
			lock_surf2->UnlockRect();
		}
		else
		{
			first_lock = !first_lock;
			//render to texture1
			device->SetRenderTarget( 0, lock_surf2 );
			device->SetDepthStencilSurface(0);
			static float verts[]= { 0, 0, 0,
				1, 1, 1,
				1, 0, 1	};
			device->BeginScene();
			device->SetFVF( D3DFVF_XYZ );
			device->DrawPrimitiveUP( D3DPT_TRIANGLELIST, 1, verts, 12 );
			device->EndScene();
			//read texture0
			D3DLOCKED_RECT locked_rect;
			if( lock_surf1->LockRect( &locked_rect, 0, D3DLOCK_NOSYSLOCK ) != D3D_OK )
				return;
			int temp_var = *((char *)locked_rect.pBits);
			lock_surf1->UnlockRect();
		}
	}
}

Hardware::DeviceObjects::~DeviceObjects()
{
	HRESULT hr;
//	hr = device->SetDepthStencilSurface( 0 );

	hr = device->SetRenderTarget( 0, render_target_default );
	hr = device->SetDepthStencilSurface( z_buffer_surface );
	hr = GlobalRelease( &z_buffer_surface );
	hr = GlobalRelease( &render_target_default );
	hr = GlobalRelease( &lock_surf1 );
	hr = GlobalRelease( &lock_surf2 );
}

void Hardware::get_data()
{}

} // namespace directx8
} // namespace hard
} // namespace lib3d
} // namespace liba

